package com.isyscore.os.metadata.controller;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.isyscore.boot.login.LoginUserManagerImpl;
import com.isyscore.boot.mybatis.PageRequest;
import com.isyscore.device.common.model.RespDTO;
import com.isyscore.os.core.exception.DataFactoryException;
import com.isyscore.os.core.exception.ErrorCode;
import com.isyscore.os.metadata.config.LicenseLimitSupport;
import com.isyscore.os.metadata.enums.DataSourceTypeEnum;
import com.isyscore.os.metadata.model.dto.DataSourceDTO;
import com.isyscore.os.metadata.model.dto.DataSourceItem;
import com.isyscore.os.metadata.model.dto.FormDataSourceDTO;
import com.isyscore.os.metadata.model.entity.DataSource;
import com.isyscore.os.metadata.model.query.DataSourceQuery;
import com.isyscore.os.metadata.model.query.DataSourceQueryWrapper;
import com.isyscore.os.metadata.model.vo.*;
import com.isyscore.os.metadata.model.vo.grpups.Insert;
import com.isyscore.os.metadata.model.vo.grpups.Test;
import com.isyscore.os.metadata.model.vo.grpups.Update;
import com.isyscore.os.metadata.service.DataSourceService;
import com.isyscore.os.metadata.service.TableAliasService;
import com.isyscore.os.metadata.service.TaskService;
import com.isyscore.os.metadata.utils.UdmpBeanUtils;
import com.isyscore.os.permission.entity.LoginVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.*;
import java.util.regex.Pattern;

/**
 * @author fjZheng
 * @version 1.0
 * @date 2020/10/21 14:45
 */
@RestController
@Api(tags = "数据源")
@RequestMapping("${api-full-prefix:}/dataSource")
@Slf4j
public class DataSourceController {
    public static final String PATTERN_EXPRESSION = "^\\w{1,30}$";
    @Resource
    private DataSourceService dataSourceService;
    @Resource
    private TableAliasService tableAliasService;

    @Resource
    private TaskService taskService;

    @Resource
    private LicenseLimitSupport licenseSupport;

    @Autowired
    private LoginUserManagerImpl loginUserManager;


    private ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
            .setNameFormat("atla-pool-%d").build();

    private ExecutorService fixedThreadPool = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors() + 1,
            Runtime.getRuntime().availableProcessors() * 40,
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>(Runtime.getRuntime().availableProcessors() * 20), namedThreadFactory);


    @GetMapping("/page")
    @ApiOperation("openAPI 数据源列表分页查询")
    public RespDTO<IPage<DataSourceItem>> page(DataSource dataSource, PageRequest page) {
        return RespDTO.onSuc(dataSourceService.pageDataSource(dataSource, page));
    }


    @PostMapping("/lock/{datasourceId}/{databaseName}/{isLocked}")
    @ApiOperation("加锁解锁")
    public void lock(@PathVariable("datasourceId") String datasourceId, @PathVariable("databaseName") String databaseName,
                     @PathVariable("isLocked") Integer isLocked) {
        dataSourceService.lock(datasourceId, databaseName, isLocked);
    }

    @PostMapping("/test")
    @ApiOperation("测试连接")
    public void test(@RequestBody @Validated(Test.class) DataSourceVO dataSourceVO) {
        DataSourceDTO dataSourceDTO = UdmpBeanUtils.copy(dataSourceVO, DataSourceDTO.class);
        dataSourceService.test(dataSourceDTO);
    }

    @PostMapping
    @ApiOperation("数据源录入")
    public void addDataSource(@RequestBody @Validated(Insert.class) DataSourceVO dataSourceVO) {
//        if (!licenseSupport.checkDataSourceLimit()) {
//            throw new DataFactoryException(ErrorCode.LICENSE_DATASOURCE_LIMIT);
//        }
        DataSourceDTO dataSourceDTO = UdmpBeanUtils.copy(dataSourceVO, DataSourceDTO.class);
        dataSourceService.addDataSource(dataSourceDTO);
    }

    @PutMapping
    @ApiOperation("数据源更新")
    public void updateDataSource(@RequestBody @Validated(Update.class) DataSourceVO dataSourceVO) {
        DataSourceDTO dataSourceDTO = UdmpBeanUtils.copy(dataSourceVO, DataSourceDTO.class);
        dataSourceService.updateDataSource(dataSourceDTO);
    }

    @GetMapping
    @ApiOperation("数据源列表")
    public IPage<DataSourceDTO> getList(DataSourceQueryWrapper query) {
        return dataSourceService.getList(query);
    }

//    @GetMapping("/{id}")
//    @ApiOperation("查询数据源")
//    public DataSource get(@PathVariable("id") Long  id) {
//        return dataSourceService.get(id);
//    }

    @GetMapping("/list")
    @ApiOperation("数据源下拉列表查询")
    public List<DataSourceDTO> getList() {
        return dataSourceService.getAll();
    }

    @DeleteMapping
    @ApiOperation("数据源移除")
    public void delBatch(@RequestBody DataSourceVO dataSourceVO) {
        dataSourceService.delBatch(dataSourceVO.getDataSourceIds());
    }

    @ApiOperation("数据源类型")
    @GetMapping("/databaseType")
    public List<JSONObject> getDatabaseType() {
        List<JSONObject> list = new ArrayList<>();
        for (DataSourceTypeEnum dbType : DataSourceTypeEnum.values()) {
            JSONObject rst = new JSONObject();
            rst.put("code", dbType.getCode());
            rst.put("name", dbType.getName());
            list.add(rst);
        }
        return list;
    }

    @ApiOperation("数据库表")
    @GetMapping("/{dataSourceId}/{databaseName}/table")
    public ResultVO getTableList(@PathVariable("dataSourceId") Long dataSourceId, @PathVariable("databaseName") String databaseName) {
        return dataSourceService.getTableList(dataSourceId, databaseName, true);
    }

    @ApiOperation("表结构")
    @GetMapping("/{dataSourceId}/{databaseName}/table/{tableName}")
    public ResultVO getTableStruct(@PathVariable("dataSourceId") Long dataSourceId, @PathVariable("databaseName")
            String databaseName, @PathVariable("tableName") String tableName) {
        return dataSourceService.getTableStruct(dataSourceId, databaseName, tableName);
    }

    @ApiOperation("表数据")
    @GetMapping("/{dataSourceId}/{databaseName}/table/{tableName}/data")
    public ResultVO getTableData(@PathVariable("dataSourceId") Long dataSourceId, @PathVariable("databaseName")
            String databaseName, @PathVariable("tableName") String tableName, DataSourceQuery query) {
        query.setDatabaseName(databaseName);
        query.setTableName(tableName);
        return dataSourceService.getTableData(dataSourceId, query);
    }

    @ApiOperation("单表查询数据（OpenAPI）")
    @PostMapping("/{dataSourceId}/{databaseName}/table/{tableName}/data")
    public RespDTO<DataRecordVo> getData(@PathVariable("dataSourceId") Long dataSourceId, @PathVariable("databaseName")
            String databaseName, @PathVariable("tableName") String tableName, @RequestBody DataQueryVO query) {
        query.setDatabaseName(databaseName);
        query.setTableName(tableName);
        ResultVO res =  dataSourceService.getTableData(dataSourceId, query);
        //剔除冗余字段
        res.getHead().stream().forEach(col -> {
            col.setName(null);
            col.setStatisticsType(null);
            col.setTableName(null);
            col.setIsNumber(null);
        });
        DataRecordVo record = new DataRecordVo();
        record.setRecords(res.getContent());
        record.setColumns(res.getHead());
        record.setCurrent(query.getCurrent());
        record.setSize(query.getSize());
        record.setTotal(res.getCount());
        return RespDTO.onSuc(record);
    }

    @ApiOperation("ER图关联")
    @GetMapping("/{dataSourceId}/{databaseName}/table/{tableName}/link")
    public JSONObject getLinkTableList(@PathVariable("dataSourceId") Long dataSourceId, @PathVariable("databaseName")
            String databaseName, @PathVariable("tableName") String tableName) {
        String taskId = taskService.creatTask(1L, TimeUnit.HOURS, null);
        JSONObject rst = new JSONObject();
        rst.put("taskId", taskId);
        LoginVO current = loginUserManager.getCurrentLoginUser();
        fixedThreadPool.submit(() -> {
            loginUserManager.setThreadLocalLoginUser(current);
            dataSourceService.getLinkTableList(dataSourceId, databaseName, tableName, taskId);
        });
        return rst;
    }

    @GetMapping("/tree/{dataSourceId}/{databaseName}")
    @ApiOperation("数据表列表")
    public List<Map<String,Object>> treeTables(@PathVariable("dataSourceId") Long dataSourceId,
                                   @PathVariable("databaseName") String databaseName) {
        return dataSourceService.getTreeTable(dataSourceId, databaseName, true);
    }

    @GetMapping("/search/{dataSourceId}/{tableName}")
    @Deprecated
    @ApiOperation("搜索表")
    public FormDataSourceDTO searchTables(@PathVariable("dataSourceId") Long dataSourceId,
                                          @PathVariable("tableName") String tableName) {
        return dataSourceService.searchTables(dataSourceId, tableName);
    }

    @PutMapping("/{dataSourceId}/{databaseName}/{tableName}")
    @ApiOperation("表名修改")
    public void renameTable(
            @PathVariable("dataSourceId") Long dataSourceId,
            @PathVariable("databaseName") String databaseName,
            @PathVariable("tableName") String tableName,
            @RequestBody TableVO tableVO) {
        tableVO.setDataSourceId(dataSourceId);
        tableVO.setDatabaseName(databaseName);
        tableVO.setOldTableName(tableName);
        dataSourceService.renameTable(tableVO);
    }


    @PostMapping("/{dataSourceId}/{databaseName}/{tableName}/{alias}")
    @ApiOperation("设置表别名")
    public void setTableAlias(
            @PathVariable("dataSourceId") Long dataSourceId,
            @PathVariable("databaseName") String databaseName,
            @PathVariable("tableName") String tableName,
            @PathVariable("alias") String alisa) {

        tableAliasService.setTableAlias(dataSourceId,databaseName,tableName,alisa);
    }

    @GetMapping("/{dataSourceId}/columnType")
    @ApiOperation("字段类型")
    public JSONArray getColumnType(@PathVariable("dataSourceId") Long dataSourceId) {
        return dataSourceService.listDataType(dataSourceId);
    }

    @GetMapping("/{dataSourceId}")
    @ApiOperation("数据源详情")
    public DataSourceDTO getDataSource(@PathVariable("dataSourceId") Long dataSourceId) {
        return dataSourceService.getDataSource(dataSourceId);
    }

    @ApiOperation("数据导入")
    @PostMapping("/import")
    public List<Map> importData(@RequestPart("file") MultipartFile file, @RequestParam("params") String params) {
        List<ExcelDataVo> parameters = JSONArray.parseArray(params, ExcelDataVo.class);
        Pattern pattern = Pattern.compile(PATTERN_EXPRESSION);
        parameters.forEach(param -> {
            if (!pattern.matcher(param.getDataTableName()).matches()) {
                throw new DataFactoryException(ErrorCode.COMMON_ERROR, "表名" + param.getDataTableName() + "不符合规范");
            }
            param.getColumnInfoList().forEach(col -> {
                String name = col.getAliasName() == null ? col.getEnName() : col.getAliasName();
                if (!pattern.matcher(name).matches()) {
                    throw new DataFactoryException(ErrorCode.COMMON_ERROR, "字段名" + name + "不符合规范");
                }
            });
        });
        return dataSourceService.importData(file, parameters);
    }

    @ApiOperation("获取模式列表")
    @GetMapping("/schema/{dataSourceId}/{databaseName}")
    public List<String> schema(@PathVariable("dataSourceId") Long dataSourceId, @PathVariable("databaseName") String databaseName) {
        return dataSourceService.schemas(dataSourceId, databaseName);
    }

    @ApiOperation("根据数据源ID导出特定的数据源信息")
    @PostMapping("/diop/export")
    public RespDTO<List<DataSource>> exportDataSourceWithIds(@RequestBody Set<Long> ids) {
        return RespDTO.onSuc(dataSourceService.exportDataSourceWithIds(ids));
    }

    @ApiOperation("导入数据源的相关信息")
    @PostMapping("/diop/import")
    public RespDTO importDataSources(@RequestBody List<DataSource> dataSources) {
        dataSourceService.importDataSources(dataSources);
        return RespDTO.onSuc();
    }

}
